package endcourseproject;
import lejos.nxt.I2CSensor;
import lejos.nxt.I2CPort;
import lejos.nxt.LCD;

public class ZigBeeZTCInterface extends I2CSensor
{
	// Constants
	public static final int MAX_MSG_SIZE = 160;
	public static final int ZTC_HDR_SIZE = 3;
	public static final int STX_ZTC_HDR_SIZE = 4;
	
	public static final byte STX = 2;
	// Registers
	/* the ZTC I2C interface is not register based 
	 * it emulates an UART like interface with messages being exchanged both ways
	 * messages are written and read from register 0 */
	private static final byte ZTC_I2CRegisterAddress = 0x00; 
	private static final int ZTC_I2C_Addr = 1;
	
	// found by trial and error - it is most likely do to buffer size in the Atmel ARM
    public static final int NXT_I2C_MAX_FRAGMENT_SIZE = 32; 
	// Variables
	
	
	public ZigBeeZTCInterface(I2CPort port)
	{
		super(port, I2CPort.STANDARD_MODE);
		// Set correct sensor type, default is TYPE_LOWSPEED
		port.setType(TYPE_LOWSPEED); // Or TYPE_LOWSPEED_9V
		
		this.setAddress(ZTC_I2C_Addr);

	}
	
	/**
	 * 
	 * @return any message ready
	 */
	public boolean messageReady()
	{		
		byte[] buffer = new byte[1];
		int status = super.getData(ZTC_I2CRegisterAddress, buffer, 1);

		if(status == 0)
		{
			byte data = buffer[0];
			/* if the data read is and STX - then there is a message ready from 
			 * the ZigBee module - all other bytes are ignored (most likely read 0)
			 */
			return (data == STX);
		}
		else
		{
			// Unable to read data for now
			return false;
		}
	}	
	
	
    private int rxOKCnt = 0;
	public boolean receiveMsg(ZTCMsg msg)
	{
  		LCD.drawString("rx msg: top  ", 0, 7);
		byte[] tmpBuf = new byte[MAX_MSG_SIZE];
		int status;
		
		status = super.getData(ZTC_I2CRegisterAddress, tmpBuf, ZTC_HDR_SIZE);
  		LCD.drawString("rx msg: top 1 ", 0, 7);
        if( status == 0 )
        {
        	msg.setOpcodeGroup(tmpBuf[0]); 
        	msg.setOpcode(tmpBuf[1]); 
        	msg.setLen((int)tmpBuf[2] & 0xFF); 
      		int i = msg.getLen()+1; // total bytes remaining in msg - data and checksum
    		int j=0;   				// progress cnt within msg
    		int nxtLen;				// lenght of next fragment
    		byte[] fragment = new byte[NXT_I2C_MAX_FRAGMENT_SIZE];
      		
        	while((j < i) && (status == 0))
        	{
        		nxtLen = i-j;;
        		if (nxtLen > fragment.length)
    				nxtLen = fragment.length;
         		LCD.drawString("rx msg: top 2 ", 0, 7);
        		status = super.getData(ZTC_I2CRegisterAddress, fragment, nxtLen);
         		LCD.drawString("rx msg: top 3 ", 0, 7);
         		 
        		// copy the fragment to the buffer
         		if(tmpBuf.length < j+nxtLen)
         		{
         			return false;
         		}
        		System.arraycopy( fragment, 0,tmpBuf, j, nxtLen);
        		// increment progress cnt
        		j += nxtLen;
        	}
     		LCD.drawString("rx msg: top 4 ", 0, 7);
        	
        	if( status == 0 )
            {
         		LCD.drawString("rx msg: top 5 ", 0, 7);
    			msg.setData(tmpBuf);	
         		LCD.drawString("rx msg: top 6 ", 0, 7);
	    		// verify checksum
         		
         		int len = msg.getLen();
         		if (len >= MAX_MSG_SIZE || len <=1) // rettet fra &&
         		{
    	      		LCD.drawString("rx msg: error", 0, 7);
         			return false;
         		}
        		LCD.drawString("rx msg: top 7 ", 0, 7);
         		
         		byte start_cs = 0;
         		try {start_cs = tmpBuf[len];}
         		catch (ArrayIndexOutOfBoundsException e) {
         			LCD.drawString("exc in receiveMsg", 0, 4);
         		}
         		boolean cs_ok = msg.VerifyCS(start_cs);
         		
         		
         		LCD.drawString("rx msg: top 8 ", 0, 7);
         		
         		LCD.drawString("rx msg: top 9 ", 0, 7);
	    		if (cs_ok)
	    		{
	    			rxOKCnt++;
  	      		  	LCD.drawString("rx msg: ok "+rxOKCnt+"   ", 0, 7);
	    		}
	    		else
	    		{
	    			status = -1; // error	
    	      		LCD.drawString("rx msg: error", 0, 7);
	    		}
            }
        	else
      		  LCD.drawString("rx msg: but  ", 0, 7);
    		
        }	
    	else
   		  LCD.drawString("rx msg: but2 ", 0, 7);

		return (status == 0);
	}

	public boolean transmitMsg(ZTCMsg msg)
	{
		byte[] tmpBuf = new byte[MAX_MSG_SIZE];
		int i = 0;
		tmpBuf[i++] = STX;
		tmpBuf[i++] = msg.getOpcodeGroup();
		tmpBuf[i++] = msg.getOpcode();
		tmpBuf[i++] = (byte)msg.getLen();
		
		try {
			for(int j = 0; j <msg.getLen() ; j++ )
			{
				tmpBuf[i++] = msg.getData()[j];
			}
			tmpBuf[i++] = msg.CalcCS();
		}
		catch (ArrayIndexOutOfBoundsException e) {
			LCD.drawString("exc in transmitMsg1", 0, 4);
		}

		int status = 0;
		//i = total bytes in msg - header, data and checksum
		int j = 0;  // progress cnt within msg
		int nxtLen; // lenght of next fragment
		byte[] fragment = new byte[NXT_I2C_MAX_FRAGMENT_SIZE];
		byte firstbyte;
		try {
			while( (j<i )&& (status == 0) )
		    {
				firstbyte = tmpBuf[j++]; // put first byte of next fragment in I2C register ID field  
				nxtLen = i-j; 			 // remaining bytes 
				if (nxtLen > fragment.length) // if remaining is more than MAX fragment then set to max 
					nxtLen = fragment.length; 
	
	    		// copy from the buffer to the fragment
				System.arraycopy( tmpBuf, j,fragment, 0, nxtLen);
	            // send data on I2C bus
				status = super.sendData(firstbyte, fragment, nxtLen);
	    		// increment progress cnt
				j+=nxtLen;
	        }
		}
		catch (ArrayIndexOutOfBoundsException e) {
			LCD.drawString("exc in transmitMsg2", 0, 4);
		}
	    	
		if (status == 0)
		{
 		  	LCD.drawString("tx msg: ok", 0, 7);
		}
		else
		{
 		  	LCD.drawString("tx msg: error", 0, 7);
 		  	LCD.drawInt(status, 13, 3);
		}	
		return (status == 0); 
	}
	
}
